#include "GEnginePCH.h"
#include "D3DApp.h"
#include "Support.h"

#define FULLSCREEN 0

D3DApp* gd3dApp              = 0;
IDirect3DDevice9* d3ddev	 = 0;

LRESULT CALLBACK WindowProc( HWND   hWnd,
							UINT   msg,
							WPARAM wParam,
							LPARAM lParam )
{
	// Don't start processing messages until the application has been created.
	if( gd3dApp != 0 )
		return gd3dApp->msgProc(msg, wParam, lParam);
	else
		return DefWindowProc(hWnd, msg, wParam, lParam);
}

D3DApp::D3DApp(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP) : fullscreen(FULLSCREEN)
{
	mMainWndCaption = winCaption;
	mDevType        = devType;
	mRequestedVP    = requestedVP;

	mhAppInst   = hInstance;
	mhMainWnd   = 0;
	md3dObject  = 0;
	mAppPaused  = false;
	ZeroMemory(&md3dPP, sizeof(md3dPP));

	bMousing = false;
	SpinY = 0.0f;
	SpinX = 0.0f;
	initMainWindow();
}

void D3DApp::initMainWindow()
{

	WNDCLASSEX wc; 
	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc   = WindowProc; 
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = mhAppInst;
	wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION); 
	wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION); 
	wc.hCursor       = LoadCursor(0, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName  = 0;
	wc.lpszClassName = "D3DWndClassName";

	if( !RegisterClassEx(&wc) )
	{
		MessageBox(0, "RegisterClass FAILED", 0, 0);
		PostQuitMessage(0);
	}

	//set up the screen in windowed or fullscreen mode?
	DWORD style;
	if (FULLSCREEN)
		style = WS_EX_TOPMOST | WS_VISIBLE | WS_POPUP;
	else
		style = WS_OVERLAPPED;

	mhMainWnd = CreateWindowEx( NULL, "D3DWndClassName", 
		"G-Engine",
		style,
		0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, wc.hInstance, NULL );




	if( !mhMainWnd )
	{
		MessageBox(0, "CreateWindow FAILED", 0, 0);
		PostQuitMessage(0);
	}

	ShowWindow(mhMainWnd, SW_SHOW);
	UpdateWindow(mhMainWnd);
}

void D3DApp::initDirect3D()
{

	MouseDown = false;
	
	// Step 1: Create the IDirect3D9 object.

	md3dObject = Direct3DCreate9(D3D_SDK_VERSION);
	if( !md3dObject )
	{
		MessageBox(0, "Direct3DCreate9 FAILED", 0, 0);
		PostQuitMessage(0);
	}

	D3DDISPLAYMODE d3ddm;

	md3dObject->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm );

	ZeroMemory( &md3dPP, sizeof(md3dPP) );

	md3dPP.BackBufferWidth            = SCREEN_WIDTH; 
	md3dPP.BackBufferHeight           = SCREEN_HEIGHT;
	md3dPP.BackBufferFormat           = d3ddm.Format;
	md3dPP.BackBufferCount            = 1;
	md3dPP.MultiSampleType            = D3DMULTISAMPLE_NONE;
	md3dPP.MultiSampleQuality         = 0;
	md3dPP.SwapEffect                 = D3DSWAPEFFECT_DISCARD; 
	md3dPP.hDeviceWindow              = mhMainWnd;
	md3dPP.Windowed                   = !fullscreen;
	md3dPP.EnableAutoDepthStencil     = TRUE; 
	md3dPP.AutoDepthStencilFormat     = D3DFMT_D24S8;
	md3dPP.Flags                      = 0;
	md3dPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	md3dPP.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;



	HRESULT dev = md3dObject->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
		mhMainWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
		&md3dPP, &d3ddev );
	if(FAILED(dev))
	{
		MessageBox( NULL, "Device wasn't created.", "ERROR", MB_OK | MB_ICONEXCLAMATION );
	}
}
int D3DApp::run()
{
	MSG  msg;
	msg.message = WM_NULL;

	__int64 cntsPerSec = 0;
	QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPerSec);
	float secsPerCnt = 1.0f / (float)cntsPerSec;

	__int64 prevTimeStamp = 0;
	QueryPerformanceCounter((LARGE_INTEGER*)&prevTimeStamp);

	while(msg.message != WM_QUIT)
	{
		// If there are Window messages then process them.
		if(PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
		// Otherwise, do animation/game stuff.
		else
		{	
			// If the application is paused then free some CPU cycles to other 
			// applications and then continue on to the next frame.
			if( mAppPaused )
			{
				Sleep(20);
				continue;
			}

			if( !isDeviceLost() )
			{
				__int64 currTimeStamp = 0;
				QueryPerformanceCounter((LARGE_INTEGER*)&currTimeStamp);
				float dt = (currTimeStamp - prevTimeStamp)*secsPerCnt;

				//updateScene(dt);
				Render();

				// Prepare for next iteration: The current time stamp becomes
				// the previous time stamp for the next iteration.
				prevTimeStamp = currTimeStamp;
			}
		}
	}
	return (int)msg.wParam;
}
LRESULT D3DApp::msgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{

	switch( msg )
	{
	case WM_KEYDOWN:
		{
			switch( wParam )
			{
			case VK_ESCAPE:
				PostQuitMessage(0);
				break;

			case VK_LEFT:	
				break; 
			case VK_RIGHT:	
				break;
			case VK_UP:
				break;
			case VK_DOWN:
				break;
			}
		}
		break;

	case WM_LBUTTONDOWN:
		{
			ptLastMousePosit.x = ptCurrentMousePosit.x = LOWORD (lParam);
			ptLastMousePosit.y = ptCurrentMousePosit.y = HIWORD (lParam);
			bMousing = true;
			MouseDown = true;
		}
		break;

	case WM_LBUTTONUP:
		{
			bMousing = false;
			MouseDown = false;
		}
		break;

	case WM_MOUSEMOVE:
		{
			ptCurrentMousePosit.x = LOWORD (lParam);
			ptCurrentMousePosit.y = HIWORD (lParam);

			if( bMousing )
			{
				SpinX -= (ptCurrentMousePosit.x - ptLastMousePosit.x);
				SpinY -= (ptCurrentMousePosit.y - ptLastMousePosit.y);
			}

			ptLastMousePosit.x = ptCurrentMousePosit.x;
			ptLastMousePosit.y = ptCurrentMousePosit.y;
		}
		break;

	case WM_CLOSE:
		{
			PostQuitMessage(0); 
		}

	case WM_DESTROY:
		{
			PostQuitMessage(0);
		}
		break;

	default:
		{
			return DefWindowProc(mhMainWnd, msg, wParam, lParam);
		}
		break;
	}

	return 0;

}

bool D3DApp::isDeviceLost()
{
	// Get the state of the graphics device.
	HRESULT hr = d3ddev->TestCooperativeLevel();

	// If the device is lost and cannot be reset yet then
	// sleep for a bit and we'll try again on the next 
	// message loop cycle.
	if( hr == D3DERR_DEVICELOST )
	{
		Sleep(20);
		return true;
	}
	// Driver error, exit.
	else if( hr == D3DERR_DRIVERINTERNALERROR )
	{
		MessageBox(0, "Internal Driver Error...Exiting", 0, 0);
		PostQuitMessage(0);
		return true;
	}
	// The device is lost but we can reset and restore it.
	else if( hr == D3DERR_DEVICENOTRESET )
	{
		InvalidateDeviceObjects();
		d3ddev->Reset(&md3dPP);
		RestoreDeviceObjects();
		return false;
	}
	else
		return false;
}
D3DApp::~D3DApp()
{
	SAFE_RELEASE(md3dObject);
	SAFE_RELEASE(d3ddev);
}